1. Les objets (Partie 1)

Cette section va être un peu longue, je vous préviens. Elle couvre enfin le concept le plus important de Java et il y a beaucoup de choses à raconter ici... Bonne lecture !

Java est un langage de programmation orienté objet. Ça signifie que les données peuvent être représentées sous formes d'objets. Un objet peut contenir de la donnée, avoir un comportement prédéfini, les objets peuvent interagir les uns avec les autres...

Dans la section dédiée aux types j'ai évoqué deux catégories de types :

  1. les types primitifs (int, double, boolean, ...)
  2. les types objets (String, List, Object, ...)

Les premiers sont au nombre de 8. En revanche, il existe potentiellement une infinité de types objets. Pourquoi ? Parce que vous pouvez créer les vôtres !
Certains sont fournis par le JDK, comme ceux listés juste au dessus (et bien plus encore !), d'autres seront créés par vous pour vos besoins, d'autres encore pourront venir d'outils mis à dispositions par d'autres développeurs. On appelle ça des librairies. On évoquera ce sujet plus tard.

À quoi servent ces types objets ?

Bon, jusque là nos données étaient assez simples, les types primitifs faisaient l'affaire pour compter ou afficher du texte, mais je vais prendre un exemple un peu plus poussé :
Imaginez que vous devez représenter un humain. Il n'y a pas de type Human pour faire ça, alors on va le créer !
Pour notre exemple, un humain aura un nom, un prénom, un age, et à chaque fois qu'il fête son anniversaire, son age augmente d'un an. On peut ainsi savoir s'il est mineur ou majeur.

Pour ça, on va créer une classe !

Créer une classe

Pour déclarer une nouvelle classe on va réutiliser le clic-droit sur le dossier src > new > Java Class :
newJavaClass.png

On va l'appeler Human :
Pasted image 20251003180307.png

Et voilà le résultat :

public class Human { }

On va pouvoir écrire le contenu de la classe entre les accolades.

public class Human {

  private String firstName;
  private String lastName;
  private int age;

}

Ces trois lignes que vous voyez entre les accolades de la classe, ce sont ses attributs (on les appelle aussi des champs !)
On les déclare un peu comme des variables classiques dans notre main, mais en plus on leur donne une visibilité. C'est une notion qui sera largement élaborée plus tard, mais pour l'instant on va se contenter de les garder avec private.

Ces attributs permettent de décrire comment on représente un humain. C'est une représentation simpliste, vous en conviendrez, mais il vaut mieux commencer simplement non ?

Comment on fait pour manipuler notre nouvelle classe Human ? Et bien comme pour n'importe quel autre type, on va déclarer une variable et l'initialiser dans notre main !

Retournons dans notre fichier Hello.java en cliquant dessus dans le panneau de gauche et modifiez sont contenu pour qu'il ressemble à ça :

void main() {
  IO.println("Hello World!");

  Human humanInstance = new Human();
  IO.println(humanInstance);
}

Puis exécutez le code comme précédemment dans la section Hello World!

Pasted image 20251101102924.png

Ça veut dire quoi ce texte : "Human@2a84aee7" ?

C'est l'affichage par défaut d'une classe. Oui, il n'est pas très beau, on n'a aucune idée de ce que c'est que ce charabia, d'où viennent les chiffres et les lettres et puis c'est quoi ce "@" au milieu ??
Je me suis aussi posé ces questions un jour, et je ne vais pas donner tous les détails tout de suite, mais je suis d'accord pour dire que c'est moche, alors changeons ça !

Une classe ne sert pas qu'à contenir des attributs, elle peut aussi contenir des méthodes pour définir son comportement !

On va ajouter une méthode dans notre classe Human :

public class Human {  
  
  private String firstName;  
  private String lastName;  
  private int age;  
  
  @Override  
  public String toString() {  
    return this.firstName + " " + this.lastName + " is " + this.age + " years old.";
  }  
}

Et maintenant on peut relancer notre programme pour voir la modification :

Pasted image 20251003184546.png
Bon, il se passe plusieurs choses ici... On va expliquer un peu.

  @Override  
  public String toString() {  
    return this.firstName + " " + this.lastName + " is " + this.age + " years old.";
  }  

En java, quand on déclare une classe, elle vient forcément avec une méthode qui s'appelle public String toString(). C'est cette méthode qui définit le comportement à avoir quand on veut représenter la classe sous forme de texte. Et c'est exactement ce qu'il se passe quand je veux l'afficher dans mon main avec IO.println(humanInstance).

Ensuite c'est quoi le contenu de cette méthode ? On voit apparaitre this : c'est le mot clé en java pour dire qu'on parle de l'instance courante. En clair, quand je veux afficher ma variable human dans le main, le toString s'active et me dit qu'il faut les attributs de cet humain.

Et enfin, pourquoi le texte affiché est "null null is 0 years old." ? D'où sortent ces "null" et ce 0 ? Eh bien tout simplement parce que nous n'avons pas initialisé ces valeurs, alors elles ont leur valeur par défaut. Dans le cas des attributs de type String c'est null et dans le cas des int c'est 0 !

Pour les initialiser proprement on va avoir besoin d'un constructeur.

Constructeur d'une classe

Un constructeur c'est une sorte de méthode qui permet d'initialiser les valeurs d'une instance de classe. Pour rappel, une instance, c'est un exemplaire concret de la classe. Dans votre main pour l'instant, votre classe Humain n'a qu'une seule instance stockée dans la variable humanInstance.
Comme on l'a vu, cette instance n'a pas encore de valeur spécifique, donc tous ses attributs sont initialisés à leur valeur par défaut (d'où les null et 0 dans l'affichage).

On va donc créer le constructeur de la classe Human :

public class Human {  
  
  private String firstName;  
  private String lastName;  
  private int age;  
  
  // Constructeur ici !
  public Human(String firstName, String lastName, int age) {  
    this.firstName = firstName;  
    this.lastName = lastName;  
    this.age = age;  
  }  
  
  @Override  
  public String toString() {  
    return this.firstName + " " + this.lastName + " is " + this.age + " years old.";  
  }  
}

En général, quand on définit un (ou des) constructeur pour une classe, il est écrit entre les attributs et les méthodes. Je vous invite à faire de même pour qu'on se retrouve avec les mêmes conventions d'écriture.

Alors, analysons ce qu'on a là...
Ça ressemble effectivement à une méthode... Un nom, des paramètres, mais pas de type de retour !
En fait, le type de retour est forcément la classe Human elle même, donc on ne le précise pas. Aussi, pour appeler un constructeur, il faut utiliser le mot clé new (comme vu dans le main).
On y retrouve encore une fois ce fameux this : c'est normal car maintenant on veut initialiser les attributs de cette instance.
Quand on lit la ligne this.firstName = firstName ​ il faut comprendre que ça signifie "l'attribut firstName de cette instance est initialisé à la valeur reçue via le paramètre firstName du constructeur"

Là par contre, vous avez sûrement une erreur qui est apparue dans votre IDE. En effet, dans le main on nous signale que la ligne Human humanInstance = new Human(); ne compile plus car il s'attend à trouver 3 arguments dans les parenthèses de new Human(). En effet, le constructeur qu'on a écrit nécessite 3 arguments : String firstName, String lastName, int age ​. Alors allons-y, on va lui en donner !

Essayez de le faire vous même sans regarder mon code immédiatement !
(Vous pouvez mettre n'importe quelle valeur du moment que ça fonctionne.)

Méthodes d'une classe

Les méthodes d'une classe servent à manipuler ses attributs ou à définir des comportements de la classe.

Par exemple, la méthode public String toString() permet de définir le comportement à avoir lorsqu'on veut représenter proprement une instance de n'importe quelle classe sous forme de texte. C'est une méthode que toutes les classes ont sans exceptions.
Par défaut, elle propose cet affichage tout moche qu'on a trouvé plus tôt : "Human@2a84aee7". En fait, par défaut, elle affiche le nom de la classe avec un arobase puis une représentation du hashCode de l'instance. C'est une notion technique qui sera abordée un peu plus tard si vous le voulez bien.
C'est grâce à cette méthode que vous verrez votre variable s'afficher proprement dans la console si vous utilisez IO.println() !

Comme elle est présente par défaut dans toutes les classes (comprenez, tous les types objets), il faut redéfinir son comportement pour qu'elle fasse les choses comme on en a envie.
C'est la signification du petit @Override juste au dessus de la méthode.
Sa présence n'est pas obligatoire pour que ça fonctionne, mais elle est très vivement recommandée (c'est moi qui vous y oblige !).
Il sert à vérifier que la méthode est bien redéfinie comme il faut. En effet, si vous l'appelez public String ToString() au lieu de public String toString() ou même public String tostring(), l'affichage ne fonctionnera pas car ce n'est pas le bon nom de méthode à redéfinir.
Eh oui ! Il faut être précis dans les termes !
Au moins, le @Override permettra d'afficher une erreur Method does not override method from its superclass. C'est un joli message pour dire que là vous essayez de redéfinir un truc qui n'existe pas donc ça va pas franchement marcher.

Vous pouvez aussi définir des méthodes personnalisées comme on l'a vu dans la section dédiée. Par exemple, on peut imaginer une méthode qui nous renvoie si notre instance est mineure !

public boolean isMinor() {  
  return this.age < 18;  // renvoie true si age < 18, false sinon
}

J'aurais aussi pu l'écrire de cette façon :

public boolean isMinor() {  
  if (this.age < 18) {  
    return true;  
  } else {  
    return false;  
  }  
}

Les deux sont valides, mais je préfère personnellement la première.

On peut noter que cette méthode est précédée d'une visibilité public mais je ne vais toujours pas m'attarder dessus car comme évoqué pour les attributs private, c'est une notion qui sera plus largement abordée plus tard. Pour le moment je vous demande de me faire confiance.

Je teste dans mon main ​ : IO.println(humanInstance.isMinor());
Ça m'affiche bien false ! (Il a 28 ans dans mon exemple)

Je peux aussi créer une méthode public void birthday() qui augmente la valeur de l'âge de 1 !
Celle-ci a un type de retour void car elle ne renvoie pas de valeur directement, elle change l'état interne de notre instance.

public void birthday() {  
  this.age++;  
}

Pour jouer avec tout ça, je teste un peu plus dans mon main ​, voilà à quoi il ressemble pour l'instant :

void main() {  
  IO.println("Hello World!");  
  
  Human humanInstance = new Human("Peter", "Parker", 28);  
  IO.println(humanInstance);  
  IO.println(humanInstance.isMinor());  
  
  Human otherInstance = new Human("Miles", "Morales", 17);  
  IO.println(otherInstance);  
  IO.println(otherInstance.isMinor());  // true  
  otherInstance.birthday();             // age + 1  
  IO.println(otherInstance.isMinor());  // false  
}
public class Human {  
  
  private String firstName;  
  private String lastName;  
  private int age;  
  
  public Human(String firstName, String lastName, int age) {  
    this.firstName = firstName;  
    this.lastName = lastName;  
    this.age = age;  
  }  

  public boolean isMinor() {  
    return this.age < 18;
  }  
  
  public void birthday() {  
    this.age++;  
  }  
  
  @Override  
  public String toString() {  
    return this.firstName + " " + this.lastName + " is " + this.age + " years old.";  
  }  
}

Conclusion

Une classe contient des données (ses champs/attributs), des comportements (ses méthodes), de quoi initialiser une instance (ses constructeurs).
Ce sont les trois principes fondamentaux pour pouvoir programmer en Java.
Certaines méthodes sont présentes dans toutes les classes par défaut (comme public String toString()) et peuvent être redéfinies pour avoir un comportement adapté au besoin.

Les détails plus complexes que j'ai rapidement évoqués comme la visibilité, le fait que des méthodes soient présentes par défaut et bien d'autres petites fonctionnalités seront approfondies dans quelques chapitres, pour pouvoir bien les appréhender.

En attendant, on va s'attaquer aux tableaux !